﻿//-----------------------------------------------------------------------
// Source File: "Form1_StatusStrip.cs"
// Create Date: 07/30/2014 10:17 AM
// Last Updated: 07/30/2014 10:17 AM
// Authors(s): nealbailey@hotmail.com
//-----------------------------------------------------------------------
// GNU GENERAL PUBLIC LICENSE
//-----------------------------------------------------------------------
// Revision: Version 3, 29 June 2007
// Copyright © 2007 Free Software Foundation, Inc. http://fsf.org/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//  REFERENCE: http://www.gnu.org/licenses/
//-----------------------------------------------------------------------
// Copyright (c) 2010-2014 Baileysoft Solutions
//-----------------------------------------------------------------------
namespace Baileysoft.WinForms
{
    using System;
    using System.Drawing;
    using System.Windows.Forms;

    public partial class Form1
    {
        #region Fields

        /// <summary>
        /// String that contains the full text (without truncation). 
        /// <para>Truncation/Expansion of status text MUST begin with the entire string. </para>
        /// <para>This variable is set in the SetSuccessStatus and SetErrorStatus methods. </para>
        /// <para>ALL changes to the status text MUST use one of these methods. </para>
        /// </summary>
        protected internal string m_lastStatusText = string.Empty;

        #endregion Fields

        #region Methods

        /// <summary>
        /// Gets the length in pixels for a String when rendered using the specified Font.
        /// </summary>
        /// <param name="text">String containing the text of interest.</param>
        /// <param name="font">Font used for rendering the string and determining it's length.</param>
        /// <returns>Length in pixels of the string, 0 if the string is null, empty, or an error occurs.</returns>
        /// <exception cref="System.ArgumentException">The exception that is thrown when one of the arguments provided to a method is not valid.</exception>
        /// <exception cref="System.ArgumentNullException">The exception that is thrown when a null reference is passed to a method that does not accept it as a valid argument.</exception>
        /// <exception cref="System.ObjectDisposedException">The exception that is thrown when an operation is performed on a disposed object.</exception>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// int length = GetTextLengthInPixels("This is a Test", SystemFonts.DefaultFont);
        /// ]]>
        /// </code>
        /// </example>
        protected internal virtual int GetTextLengthInPixels(string text, Font font)
        {
            Graphics g = this.CreateGraphics();
            SizeF textSize = g.MeasureString(text, font);
            return (int) textSize.Width;
        }

        /// <summary>
        /// Event handler for when the Form's Status Strip (status bar) has been resized.
        /// </summary>
        /// <param name="sender">sender of the event.</param>
        /// <param name="e">event arguments.</param>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// statusStrip1_Resize(this, EventArgs.Empty);
        /// ]]>
        /// </code>
        /// </example>
        protected internal virtual void StatusStrip1Resize(object sender, EventArgs e)
        {
            TruncateStatusTextToFit();
        }

        /// <summary>
        /// Truncates the status bar text to fit within the available space. 
        /// <para>'...' is appended when the text is truncated.</para>
        /// </summary>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// TruncateStatusTextToFit();
        /// ]]>
        /// </code>
        /// </example>
        protected internal virtual void TruncateStatusTextToFit()
        {
            try
            {
                // these will cause exceptions that will be handled,
                // however we want to avoid the extraneous log entries.
                // (the status strip may have been removed, or is being replaced @ runtime)
                if (statusStrip1 == null)
                    return;
                if (tspStatusLabel == null)
                    return;
                if (m_lastStatusText == null)
                    return;

                // always pass all of the text
                TruncateStatusTextToFit(m_lastStatusText, statusStrip1, tspStatusLabel);
            }
            catch (Exception ex) //Urgent: empty catch
            {
                //Log.Error("Error while truncating text to fit within the status bar: " + ex.Message);
                LogMessageToWindow("Error while truncating text to fit within the status bar: " + ex.Message);
            }
        }

        /// <summary>
        /// Truncates the status bar text to fit within the available space. 
        /// <para>'...' is appended when the text is truncated.</para>
        /// </summary>
        /// <param name="fullText">Complete (non-truncated) text to display.</param>
        /// <param name="statusStrip">StatusStrip object that contains the passed ToolStripStatusLabel object.</param>
        /// <param name="statusLabel">ToolStripStatusLabel object that will display the status bar text.</param>
        /// <exception cref="System.ArgumentException">The exception that is thrown when one of the arguments provided to a method is not valid.</exception>
        /// <exception cref="System.ArgumentNullException">The exception that is thrown when a null reference is passed to a method that does not accept it as a valid argument.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">The exception that is thrown when the value of an argument is outside the allowable range of values as defined by the invoked method.</exception>
        /// <exception cref="NullReferenceException">The exception that is thrown when there is an attempt to dereference a null object reference.</exception>
        /// <exception cref="ObjectDisposedException">The exception that is thrown when an operation is performed on a disposed object.</exception>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// try
        /// {
        ///     string fullText = "Now is the winter of our discontent.";
        ///
        ///     // example code only - typically already in your Form class's designer.cs file
        ///     StatusStrip statusStrip = new StatusStrip();                   
        ///     ToolStripStatusLabel statusLabel = new ToolStripStatusLabel();
        ///     statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {statusLabel});
        ///     
        ///     TruncateStatusTextToFit(fullText, statusStrip, statusLabel);
        /// }
        /// catch (Exception ex)
        /// {
        ///     Console.WriteLine("Error while truncating text to fit within the status bar: " + ex.Message);
        /// }
        /// ]]>
        /// </code>
        /// </example>
        protected internal virtual void TruncateStatusTextToFit(string fullText, StatusStrip statusStrip, ToolStripStatusLabel statusLabel)
        {
            var temp = fullText;

            // don't show item tooltips - will be enabled if we truncate
            statusStrip.ShowItemToolTips = false;
            // status text width in pixel - use the status label's font
            var textWidth = GetTextLengthInPixels(temp, statusLabel.Font);

            // Maximum width of text in pixels that is allowed.
            // width of the status bar, minus the width of the status label's image,
            // minus the width of the gripper, minus 4 (a little gap)
            var gripperWidth = (statusStrip.SizeGripBounds == null) ? 0 : statusStrip.SizeGripBounds.Width;
            var statusImageWidth = (statusLabel.Image == null) ? 0 : statusLabel.Image.Width;
            var maxTextWidth = statusStrip.Size.Width - statusImageWidth - gripperWidth - 4;
            if (maxTextWidth < 0)
                maxTextWidth = 0;

            // need to know if the text was truncated, we will need to append '...' if we truncated the text
            var truncated = false;
            while ((textWidth > maxTextWidth) &&
                   (temp.Length > 0))
            {
                temp = temp.Substring(0, temp.Length - 1);
                temp = temp.TrimEnd(); // prevents ... from being preceded by space(s)
                truncated = true; // yup, we truncated the text
                // recalculate width, include the '...' that will be appended later
                textWidth = GetTextLengthInPixels(temp + "...", tspStatusLabel.Font);
            }

            if (truncated)
            {
                temp += "...";
                // show item tooltip if the status text is truncated
                statusStrip.ShowItemToolTips = true;
                statusLabel.ToolTipText = fullText;
            }

            // set the status bar text
            statusLabel.Text = temp;
        }

        /// <summary>
        /// Gets the length in pixels for a String when rendered using the specified Font.
        /// </summary>
        /// <param name="text">String containing the text of interest.</param>
        /// <param name="font">Font used for rendering the string and determining it's length.</param>
        /// <param name="length">Length in pixels of the string, 0 if the string is null, empty, or an error occurs.</param>
        /// <returns>Length in pixels of the string, 0 if the string is null, empty, or an error occurs.</returns>
        /// <returns>True if the method executed without error, false otherwise.</returns>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// int length = 0;
        /// if (!TryGetTextLengthInPixels("This is a Test", SystemFonts.DefaultFont, out length);
        ///     Console.WriteLine("Failed to get the string's length in pixels.");
        /// ]]>
        /// </code>
        /// </example>
        protected internal virtual bool TryGetTextLengthInPixels(string text, Font font, out int length)
        {
            length = 0;
            Exception exception = null;
            return TryGetTextLengthInPixels(text, font, out length, out exception);
        }

        /// <summary>
        /// Gets the length in pixels for a String when rendered using the specified Font.
        /// </summary>
        /// <param name="text">String containing the text of interest.</param>
        /// <param name="font">Font used for rendering the string and determining it's length.</param>
        /// <param name="length">Length in pixels of the string, 0 if the string is null, empty, or an error occurs.</param>
        /// <param name="exception">Exception that caused the false return from this method.</param>
        /// <returns>True if the method executed without error, false otherwise.</returns>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// int length = 0;
        /// Exception exception = null;
        /// if (!TryGetTextLengthInPixels("This is a Test", SystemFonts.DefaultFont, out length, out exception);
        ///     Console.WriteLine("Failed to get the string's length in pixels: " + exception.Message);
        /// ]]>
        /// </code>
        /// </example>
        protected internal virtual bool TryGetTextLengthInPixels(string text, Font font, out int length,
            out Exception exception)
        {
            bool result = false;
            length = 0;
            exception = null;
            try
            {
                length = GetTextLengthInPixels(text, font);
                result = true;
            }
            catch (Exception ex)
            {
                exception = ex;
                //Log.Error("Error getting the width in pixels for '" + text + "': " + ex.Message);
            }

            return result;
        }

        #endregion Methods
    }
}